package gbench.sandbox.plot.geom;

import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jblas.DoubleMatrix;
import gbench.common.matlib.MatlibCanvas;
import gbench.common.matlib.MatlibCanvas.NVec;

import static java.lang.Math.PI;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static gbench.common.tree.LittleTree.compose_f;
import static gbench.sandbox.plot.geom.JunitEllipseAffine.Mx.*;
import static gbench.common.matlib.MatlibCanvas.Range.*;


/**
 * 绘制几何图形，制作一个椭圆，由一条直线，转成一个椭圆
 * @author gbench
 *
 */
public class JunitEllipseAffine {
    
    /**
     * 简单矩阵
     * @author gbench
     *
     */
    public static class Mx {
        
        /**
         * 一维向量
         * 
         * @param dd
         * @return
         */
        public static DoubleMatrix dblmx(double... dd) {
            return new DoubleMatrix(dd);
        }

        /**
         * 
         * @param dd
         * @return
         */
        public static DoubleMatrix dblmx(final double[][] dd) {
            return new DoubleMatrix(dd);
        }
        
        /**
         * 对角阵
         */
        public static DoubleMatrix diag(double... dd) {
            return DoubleMatrix.diag(new DoubleMatrix(dd));
        }
        
        /**
         * 生成一个5*5的[R,I] 旋转矩阵(左乘结构)    <br>
         * R 是一个2x2维的旋转子矩阵:<br>
         * cos(theta)    -sin(theta)        <br>
         * sin(theta)    cos(theta)        <br>
         * I 是一个3x3维的单位矩阵。             <br>
         * @param <T> 返回值的类型即mapper的结果类型
         * @param theta 旋转角度(正方向为逆时针方向)
         * @param mapper 矩阵变换器 : mx 5*5矩阵 -> t
         * @return 旋转矩阵[R,I]
         */
        public static <T> T rotateMx(final double theta, final Function<double[][],T> mapper) {
            
            final var mx = new double[][] { // 旋转矩阵
                //列向量的变换分块矩阵:坐标旋转
                {cos(theta), -sin(theta),     0d,0d,0d},
                {sin(theta), cos(theta),      0d,0d,0d},
                // 列向量的保留分块矩阵:颜色数据矩阵
                {0d,         0d,              1d,0d,0d},
                {0d,         0d,              0d,1d,0d},
                {0d,         0d,              0d,0d,1d}
            };
            
            return mapper.apply(mx);
        }
        
        /**
         * 生成一个5*5的[R,I] 旋转矩阵(右乘结构)    <br>
         * R 是一个2x2维的旋转子矩阵:<br>
         * cos(theta)     sin(theta)        <br>
         * -sin(theta)    cos(theta)        <br>
         * I 是一个3x3维的单位矩阵。             <br>
         * @param <T> 返回值的类型即mapper的结果类型
         * @param theta 旋转角度(正方向为逆时针方向)
         * @param mapper 矩阵变换器 : mx 5*5矩阵 -> t
         * @return 旋转矩阵[R,I]
         */
        public static <T> T rotateMx2(final double theta, final Function<double[][],T> mapper) {
            
            final var mx = new double[][] { // 旋转矩阵
                //列向量的变换分块矩阵:坐标旋转
                {cos(theta),  sin(theta),       0d,0d,0d},
                {-sin(theta), cos(theta),       0d,0d,0d},
                // 列向量的保留分块矩阵:颜色数据矩阵
                {0d,          0d,               1d,0d,0d},
                {0d,          0d,               0d,1d,0d},
                {0d,          0d,               0d,0d,1d}
            };
            
            return mapper.apply(mx);
        }

        /**
         * 核心函数
         * 
         * @param <T>    mapper 的 值类型
         * @param matrix 浮点类型的矩阵
         * @param mapper 行变换函数
         * @return T类型的流
         */
        public static <T> Stream<T> stream(final DoubleMatrix matrix, final Function<double[], T> mapper) {
            return Arrays.stream(matrix.toArray2()).map(mapper);
        }

    }
    
    /**
     * 绘图操作
     */
    public static void plot() {
        final var size = 1000; // 画布的绘图范围
        final var matlib = new MatlibCanvas(size);
        matlib.plot(size, size/2, pen->{
            final var points = Math.random()>0.5
                ? P2(0,500,500).map(matlib.fx(x->100*sin(6*PI*x/500))) // 一条正弦曲线
                : cph(P2(0,500,3),P2(-PI,PI,5000)).map(matlib.rhotheta2_affine_scale(1, 1/3d)); // 3个同心椭圆
            
            pen.drawPoints2(points, // 画出一条水平线，然后给与拉伸变换。
                compose_f(DoubleMatrix::new,NVec::toArray), pointMx -> { // 把这条 直线转换成一个 点向量矩阵
                return P2(-PI, PI,50).flatMap( theta -> stream ( // 把这条直线[点向量矩阵]旋转1个圆周：一周被分解成2000分
                    pointMx.mmul(rotateMx2(theta, Mx::dblmx)) // 旋转
                        .mmul(diag(1d, 1d / 3, 1d, 1d, 1d)) // 缩放坐标轴,注意为保持没漏洞只能缩，不能放,因为被离散后就不连续了。
                        .addRowVector(dblmx(500, 200d, 0d, 0d, 0d)) // 平移到画面中心
                        ,compose_f(NVec::ints,NVec::new))
                ).collect(Collectors.toList());
            });// drawPoints2
        });
    }
    
    /**
     * 绘制一个椭圆
     * @param args
     */
    public static void main(String args[]) {
        plot();
    }

}
